﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.Security.Permissions;
using System.Web;
using DBFile.Core.Interfaces;

namespace DBFile.Core.Configuration
{
	/// <summary>
	/// Provides the Provider tie to the section handler of the config file
	/// </summary>
	[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
		AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
	public abstract class DBFileProvider : ProviderBase, IDBFileProvider
	{
        private const string ProviderDescriptionKey = "description";
        private const string UseCompressionKey = "useCompression";

        private bool useCompression;

		private static DBFileProvider provider;
		private static DBFileProviderCollection providers;

		static DBFileProvider()
		{
			providers = DBFileProviderCollection.ReadDBFileProviderSection();
            if (providers.Count > 0)
            {
                provider = providers[providers.DefaultProviderName];
            }
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="DBFileProvider"/> class.
		/// </summary>
		protected DBFileProvider()
		{ }

		/// <summary>
		/// Instances of the provider.
		/// </summary>
		/// <returns></returns>
		public static DBFileProvider Instance()
		{
            if (provider == null && providers.Count > 0)
            {
                provider = providers[providers.DefaultProviderName];
            }
			return provider;
		}

		/// <summary>
		/// Gets the providers.
		/// </summary>
		/// <value>The providers.</value>
		public static DBFileProviderCollection Providers
		{
			get { return providers; }
		}

		/// <summary>
		/// Initializes the specified name.
		/// </summary>
		/// <param name="name">The name.</param>
		/// <param name="configValue">The config value.</param>
		public override void Initialize(string name, NameValueCollection configValue)
		{
            if (configValue == null)
            {
                throw new ArgumentNullException("configValue");
            }

            OnBeforeInitialize(configValue);
			
            base.Initialize(name, configValue);

            OnAfterInitialize(configValue);
		}

        public bool UseCompression { get; set; }
        
        protected virtual void OnBeforeInitialize(NameValueCollection configValue)
        {
            if (string.IsNullOrEmpty(configValue[ProviderDescriptionKey]))
            {
                configValue.Remove(ProviderDescriptionKey);
                configValue.Add(ProviderDescriptionKey, ProviderDescription);
            }

            UseCompression = GetConfigSettingAsBool(configValue, UseCompressionKey);
        }

        protected virtual void OnAfterInitialize(NameValueCollection configValue)
        { }

        protected abstract string ProviderDescription { get; }

        protected static bool GetConfigSettingAsBool(NameValueCollection config, string keyName)
        {
            if (!string.IsNullOrEmpty(config[keyName]))
            {
                bool tempResult;
                if (bool.TryParse(config[keyName], out tempResult))
                {
                    return tempResult;
                }
            }
            return false;
        }

        protected static string GetConfigSettingOrThrow(NameValueCollection config, string keyName, string message)
        {
            if (string.IsNullOrEmpty(config[keyName]))
            {
                throw new ProviderException(message);
            }
            return config[keyName];
        }

        protected static string GetConnectionString(NameValueCollection config, string name, string missingNameMessage, string missingConnectionString)
        {
            string connectionStringName = GetConfigSettingOrThrow(config, name, missingNameMessage);

            if (string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[name].ConnectionString))
            {
                throw new ProviderException(missingConnectionString);
            }
            config.Remove(name);

            return ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
        }


		/// <summary>
		/// Existses the specified filename.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <returns></returns>
		public abstract bool Exists(string filename);

		/// <summary>
		/// Copies the specified source filename.
		/// </summary>
		/// <param name="sourceFilename">The source filename.</param>
		/// <param name="destinationFilename">The destination filename.</param>
		/// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
		/// <returns></returns>
		public abstract int Copy(string sourceFilename, string destinationFilename, bool overwrite);

		/// <summary>
		/// Inserts the specified filename.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <param name="buffer">The buffer.</param>
		/// <param name="contentType">Type of the content.</param>
		/// <param name="contentLength">Length of the content.</param>
		/// <returns></returns>
		public abstract int Insert(string filename, byte[] buffer, string contentType, long contentLength);

		/// <summary>
		/// Inserts the specified filename.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <param name="buffer">The buffer.</param>
		/// <param name="contentType">Type of the content.</param>
		/// <param name="contentLength">Length of the content.</param>
		/// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
		/// <returns></returns>
		public abstract int Insert(string filename, byte[] buffer, string contentType, long contentLength, bool overwrite);

		/// <summary>
		/// Updates the specified filename.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <param name="buffer">The buffer.</param>
		/// <param name="contentType">Type of the content.</param>
		/// <param name="contentLength">Length of the content.</param>
		/// <returns></returns>
		public abstract int Update(string filename, byte[] buffer, string contentType, long contentLength);

		/// <summary>
		/// Deletes the specified filename.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <returns></returns>
		public abstract int Delete(string filename);

		/// <summary>
		/// Gets the list of file names.
		/// </summary>
		/// <param name="appPath">The app path.</param>
		/// <returns></returns>
        public abstract string[] GetFileNameList();

		/// <summary>
		/// Gets the specified filename.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <returns></returns>
        public abstract T Get<T>(string filename) where T : IDBFile, new();
	}
}
